#include "StdAfx.h"
#include "EphemWrapper.h"

CBufferHolder::CBufferHolder(FILE * file, int nBufSize)
: m_File(file)
{
	if(m_File == NULL)
		throw CCustomException("Empty File");
	if(nBufSize == 0)
		throw CCustomException("zero size");

	for(int i = 0; i < sizeof(m_buffer)/sizeof(m_buffer[0]); i++)
	{
		m_buffer[i].last = (i == 0)? true: false;
		m_buffer[i].Offset = -1;
		m_buffer[i].vec.resize(nBufSize);
	}
}

double *CBufferHolder::GetBufferAtOffset(int nOffset)
{
	if(nOffset < 0)
		throw CCustomException("offset less then zero");
		
	for(int i=0; i < sizeof(m_buffer)/sizeof(m_buffer[0]); i++)
	{
		if(	m_buffer[i].Offset == nOffset)
			return &(m_buffer[i].vec[0]);
	}

	Buffer * buf = (m_buffer[0].last)? (m_buffer+1): (m_buffer);
	fseek(m_File, nOffset, SEEK_SET);

	size_t i = fread(&(buf->vec[0]), sizeof(buf->vec[0]), buf->vec.size(), m_File);
	if (i < buf->vec.size() && feof(m_File)) 
		throw CCustomException("error reading: read %d koef from %d%d", i, buf->vec.size(), (feof(m_File))? " EOF": "");
	else
	{
		for(int i=0; i < sizeof(m_buffer)/sizeof(m_buffer[0]); i++)
			m_buffer[i].last = false;
		buf->last = true;
		buf->Offset = nOffset;
		return &(buf->vec[0]);
	}
}

// ====================================== //

CEphemWrapper::CEphemWrapper(LPCTSTR stzEphemFileName)
: m_file(stzEphemFileName, _T("rb"))
{
	ephcom_readbinary_header ();
	//m_vecDataBlock.resize(m_header.ncoeff);
	m_ReadBlockBuff = CBufferHolderAP(new CBufferHolder(m_file, m_header.ncoeff));

	m_vecPos.resize(m_header.maxcheby);
	m_vecVel.resize(m_header.maxcheby);
	
	LPCTSTR stzName [] = { 
		_T("GM1"), 
		_T("GM2"), 
		_T("GMB"),
		_T("GM4"), 
		_T("GM5"), 
		_T("GM6"), 
		_T("GM7"), 
		_T("GM8"), 
		_T("GM9"), 
		_T("EMRAT"), 
		_T("GMS") };

	if(sizeof(stzName)/sizeof(stzName[0]) != sizeof(m_dGM)/sizeof(m_dGM[0]))
		throw CCustomException(_T("wrong BODY_COUNT value, must be 12"));

	for(int i = 0; i<BODY_COUNT; i++)
	{
		m_dGM[i] = GetParam(stzName[i]);
	}

	m_dGM[eMOON] = m_dGM[eEARTH] / (1.0 + m_dGM[eMOON]);
	m_dGM[eEARTH] -= m_dGM[eMOON] ;
}

CEphemWrapper::~CEphemWrapper(void)
{
}

bool CEphemWrapper::GetCoord(double date, bool bBaryCenter, AllBodies & OutCoords, bool bGetVelocity)
{
	if(date <= m_header.ss[0])
		throw CCustomException(_T("date less then ephemerid range"));

	if(date >= m_header.ss[1])
		throw CCustomException(_T("date great then ephemerid range"));

	memset( &OutCoords, 0, sizeof(AllBodies));

	struct ephcom_Coords coords;
	coords.bary = bBaryCenter? 1: 0;
	coords.center = EPHCOM_SUN;
	coords.km = 0;        /* Use AU, not kilometers */
	coords.seconds = 0;   /* Timescale is days, not seconds */
	coords.et2[0] = date;  /* Good enough precision for test dates */
	coords.et2[1] = 0.0;

	if ( ephcom_get_coords(&coords, bGetVelocity) ) 
		return false;

	for(int i = 0; i<BODY_COUNT; i++)
	{
		OutCoords.bodies[i].GM = m_dGM[i];
		memcpy(OutCoords.bodies[i].x, coords.pv[i]    , sizeof(OutCoords.bodies[i].x) );
		if(bGetVelocity)
			memcpy(OutCoords.bodies[i].v, coords.pv[i] + 3, sizeof(OutCoords.bodies[i].v) );
	}

	return true;
}

double CEphemWrapper::GetParam(LPCTSTR stzName)
{
	int len = _tcslen(stzName);
	if(len > 6)
		throw CCustomException(_T("too long"));

	for(int pos = 0; pos < sizeof(m_header.cnam)/sizeof(m_header.cnam[0]); ++pos)
	{
		if(memcmp(m_header.cnam[pos], stzName, len*sizeof(stzName[0])) == 0)
			return m_header.cval[pos];
	}

	throw CCustomException(_T("not found"));
}

CStdString CEphemWrapper::StrDateFromJD(double jd)
{
	CStdString res;
	int year, month, date;
	double daypart;
	DateFromJD(jd, year, month, date, daypart);
	daypart += date;
	res.Format(_T("%04d-%02d-%09.6lf"), year, month, daypart);
	return res;
}

LPCTSTR CEphemWrapper::GetBodyName(eBodies body)
{
	LPCTSTR res = _T("");
	switch (body)
	{
	case eMERCURY:
		res = _T("Mercury");
		break;
	case eVENUS:
		res = _T("Venus");
		break;
	case eEARTH :
		res = _T("Earth");
		break;
	case eMARS:
		res = _T("Mars");
		break;
	case eJUPITER:
		res = _T("Jupiter");
		break;
	case eSATURN:
		res = _T("Saturn");
		break;
	case eURANUS:
		res = _T("Uranus");
		break;
	case eNEPTUNE:
		res = _T("Neptune");
		break;
	case ePLUTO:
		res = _T("Pluto");
		break;
	case eMOON:
		res = _T("Moon");
		break;
	case eSUN:
		res = _T("Sun");
		break;
	default:
		res = _T("Unknoun");
	}
	return res;
}

bool  CEphemWrapper::DateFromJD(double tjd, int &year, int &month, int &date, double &daypart)
{
	int j;
	int calendar_type = 0;
	/* From Explanatory Supplement to Astronomical Almanac, pp. 604, 606 */
	int I, J, K, L, N, D, M, Y;

	tjd += 0.5;
	j = (int)tjd;  /* Integer Julian Day */
	daypart = (tjd - j);

	/*
	Julian calendar.  Explanatory Supplement to Astronomical Alamanac, p. 606.
	If automatic, use Julian calendar for dates before 15 October 1582.
	*/
	if (calendar_type == -1 || (calendar_type == 0 && j <= 2299160)) 
	{
		J = j + 1402;
		K = (J - 1) / 1461;
		L = J - 1461 * K;
		N = (L - 1) / 365 - L / 1461;
		I = L - 365 * N + 30;
		J = (80 * I) / 2447;
		D = I - (2447 * J) / 80;
		I = J / 11;
		M = J + 2 - 12 * I;
		Y = 4 * K + N + I - 4716;
	}
	/*
	Gregorian calendar.
	*/
	else  /* Explanatory Supplement to Astronomical Almanac, p. 604 */
	{	
		L = j + 68569;
		N = (4 * L) / 146097;
		L = L - (146097 * N + 3) / 4;
		I = (4000 * (L + 1)) / 1461001;
		L = L - (1461 * I) / 4 + 31;
		J = (80 * L) / 2447;
		D = L - (2447 * J) / 80;
		L = J / 11;
		M = J + 2 - 12 * L;
		Y = 100 * (N - 49) + I + L;
	}

	year = Y;
	month = M;
	date = D;

	return true;
}

const double CEphemWrapper::KmInAu = 0.149597870699626200e+09;

double CEphemWrapper::AUtoKM(double au)
{
	return KmInAu * au;
}

double CEphemWrapper::KMtoAU(double km)
{
	return km / KmInAu;
}
